﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using System.Xml;
using System.Xml.Linq;
using System.IO;
using Agility=HtmlAgilityPack;
using System.Net;

namespace IndustrialMusicWindowsCSharp {
    public partial class ProcessS107Links : Form {
        XElement artists = XElement.Load("Samples.xml");
        delegate void SetTextCallback(string text);

        public ProcessS107Links() {
            InitializeComponent();
        }

        private void btnConversionFolderSelect_Click(object sender, EventArgs e) {
            DialogResult result = openProcessS107.ShowDialog();
            if (result == DialogResult.OK) {
                txtConversionFolder.Text = openProcessS107.FileName;
            }
        }

        private void btnSelectDestination_Click(object sender, EventArgs e) {
            DialogResult result = saveProcessS107.ShowDialog();
            if (result == DialogResult.OK) {
                txtConversionDestinationFolder.Text = saveProcessS107.FileName;
            }
        }

        private void btnCancel_Click(object sender, EventArgs e) {
            this.Close();
        }

        private void btnConvert_Click(object sender, EventArgs e) {
            backgroundWorker1.RunWorkerAsync(txtConversionFolder.Text.Trim());
        }

        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) {
            for (Int64 i = 0; i <= 25; i++) {
                string alphabetCode = Utilities.Base26Encode(i);

                StreamReader fileReader = new StreamReader(e.Argument.ToString().Substring(0,e.Argument.ToString().LastIndexOf(@"\")) + @"\" + alphabetCode + ".txt");
                string fileContents = fileReader.ReadToEnd();
                fileReader.Close();
                fileReader.Dispose();

                ProcessFile(e.Argument.ToString(), fileContents);

                if (this.txtFileProcessed.InvokeRequired) {
                    // It's on a different thread, so use Invoke.
                    SetTextCallback d = new SetTextCallback(SetText);
                    this.Invoke
                        (d, new object[] { alphabetCode + ".txt" + System.Environment.NewLine });
                } else {
                    // It's on the same thread, no need for Invoke
                    this.txtFileProcessed.Text = alphabetCode + ".txt" + System.Environment.NewLine;
                }
            }
        }

        private void ProcessFile(string filePath, string fileContents) {
            XElement xmlDocument = XElement.Load(filePath);

            Agility.HtmlDocument document = new HtmlAgilityPack.HtmlDocument();
            document.LoadHtml(fileContents);

            Agility.HtmlNode artistNode = document.DocumentNode.SelectSingleNode("//div[@id='menu_artists']");

            Agility.HtmlDocument artistDocument = new HtmlAgilityPack.HtmlDocument();
            artistDocument.LoadHtml(artistNode.OuterHtml);

            Agility.HtmlNodeCollection artists = artistDocument.DocumentNode.SelectNodes("//ul/li");

            Regex linkRegex = new Regex(@"\<li\>\<a href\=\x22(.*?)\x22\>(.*?)\<\/a\>\<\/li\>");

            foreach(Agility.HtmlNode node in artists){
                foreach(Match match in linkRegex.Matches(node.OuterHtml)){
                    string linkHref = match.Groups[1].Value;
                    string linkText = match.Groups[2].Value;

                    XElement linkElement = new XElement("link");
                    XAttribute linkAttribute = new XAttribute("href", linkHref);
                    XAttribute textAttribute = new XAttribute("text", linkText);
                    linkElement.Add(linkAttribute);
                    linkElement.Add(textAttribute);
                    xmlDocument.Add(linkElement);
                }
            }

            xmlDocument.Save(filePath);
        }

        private void SetText(string text) {
            this.txtFileProcessed.Text += text;
        }



        private void screenscrapeLinksBackgroundWorker_DoWork(object sender, DoWorkEventArgs e) {
            ScreenscrapeFiles(e.Argument.ToString());
        }

        private void ScreenscrapeFiles(string conversionFilePath) {
            string filePath = conversionFilePath.Substring(0, conversionFilePath.LastIndexOf(@"\"));

            XElement xmlDocument = XElement.Load(filePath + @"\s107Links.xml");

            var links = from link in xmlDocument.Descendants("link")
                        select link;

            foreach (var link in links) {
                ScreenScrapeLink(link.Attribute("href").Value,link.Attribute("href").Value.Substring(0,link.Attribute("href").Value.LastIndexOf('.')));
            }
        }

        private void btnScreenScrape_Click(object sender, EventArgs e) {
            screenscrapeLinksBackgroundWorker.RunWorkerAsync(txtConversionFolder.Text);
        }

        private void ScreenScrapeLink(string linkFile, string fileName) {
            WebClient browser = new WebClient();

            Uri websiteAddress = new Uri(@"http://s107.net/" + linkFile);

            try {
                byte[] pageContents = browser.DownloadData(websiteAddress);
                ProcessScreenScrapeContents(pageContents, fileName);
            } finally {
                browser.Dispose();
            }
        }

        private void ProcessScreenScrapeContents(byte[] pageContents, string fileName) {
            UTF8Encoding encoder = new UTF8Encoding();
            string contents = encoder.GetString(pageContents);

            if (contents.Length != 0) {
                StreamWriter writer = new StreamWriter(fileName + ".txt");
                writer.Write(contents);
                writer.Close();
                writer.Dispose();
            }            
        }

        private void screenscrapeLinksBackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
            MessageBox.Show("Screen scraping complete!");
        }

        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
            MessageBox.Show("Conversion of files complete.");
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sampleText"></param>
        /// <param name="sourceType"></param>
        /// <returns></returns>
        private XElement CreateSampleNode(string sampleText, string sourceText, string sourceType, string sourceTimeLocation) {
            XElement sampleElement = new XElement("sample");
            XElement textElement = new XElement("text", sampleText);
            XElement sourceElement = new XElement("source", sourceText);
            XAttribute sampleTimeLocationAttribute = new XAttribute("timelocation", sourceTimeLocation);
            XAttribute sourceTypeAttribute = new XAttribute("sourcetype", sourceType);

            sourceElement.Add(sourceTypeAttribute);
            sampleElement.Add(sampleTimeLocationAttribute);
            sampleElement.Add(textElement);
            sampleElement.Add(sourceElement);

            return sampleElement;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="songName"></param>
        /// <returns></returns>
        private XElement CreateSongNode(string songName) {
            XElement songElement = new XElement("song");
            XAttribute nameAttribute = new XAttribute("name", songName);

            songElement.Add(nameAttribute);

            return songElement;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="releaseName"></param>
        /// <returns></returns>
        private XElement CreateReleaseNode(string releaseName) {
            XElement releaseElement = new XElement("release");
            XAttribute nameAttribute = new XAttribute("name", releaseName);

            releaseElement.Add(nameAttribute);

            return releaseElement;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="artistName"></param>
        /// <returns></returns>
        private XElement CreateArtistNode(string artistName) {
            XElement artistElement = new XElement("artist");
            XAttribute nameAttribute = new XAttribute("name", artistName);

            artistElement.Add(nameAttribute);

            return artistElement;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="artists"></param>
        /// <param name="artistName"></param>
        /// <returns></returns>
        private XElement GetArtist(XElement artists, string artistName) {
            XElement artistResult = null;

            var query = from artist in artists.Descendants("artist")
                        where artist.Attribute("name") != null &&
                            artist.Attribute("name").Value == artistName
                        select artist;

            if (query.Count<XElement>() == 1) {
                artistResult = query.First<XElement>();
            }

            return artistResult;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="artists"></param>
        /// <param name="artistName"></param>
        /// <param name="releaseName"></param>
        /// <returns></returns>
        private XElement GetRelease(XElement artists, string artistName, string releaseName) {
            XElement releaseResult = null;

            var query = from artist in artists.Descendants("artist")
                        where artist.Attribute("name") != null &&
                            artist.Attribute("name").Value == artistName
                        from release in artist.Descendants("release")
                        where release.Attribute("name") != null &&
                            release.Attribute("name").Value == releaseName
                        select release;

            if (query.Count<XElement>() == 1) {
                releaseResult = query.First<XElement>();
            }

            return releaseResult;
        }

        private XElement GetRelease(XElement artist, string releaseName) {
            XElement releaseResult = null;

            var query = from release in artist.Descendants("release")
                        where release.Attribute("name") != null &&
                            release.Attribute("name").Value == releaseName
                        select release;

            if (query.Count<XElement>() == 1) {
                releaseResult = query.First<XElement>();
            }

            return releaseResult;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="artists"></param>
        /// <param name="artistName"></param>
        /// <param name="releaseName"></param>
        /// <param name="songName"></param>
        /// <returns></returns>
        private XElement GetSong(XElement artists, string artistName, string releaseName, string songName) {
            XElement songResult = null;

            var query = from artist in artists.Descendants("artist")
                        where artist.Attribute("name") != null &&
                            artist.Attribute("name").Value == artistName
                        from release in artist.Descendants("release")
                        where release.Attribute("name") != null &&
                            release.Attribute("name").Value == releaseName
                        from song in release.Descendants("song")
                        where song.Attribute("name") != null &&
                            song.Attribute("name").Value == songName
                        select song;

            if (query.Count<XElement>() == 1) {
                songResult = query.First<XElement>();
            }

            return songResult;
        }

        private void SaveSampleInformation(string artistName, string songTitle, string songAlbum, string songSampleTime, string songSample, string songSourceName) {
            string myString = string.Empty;

            XElement artist = null;
            XElement release = null;
            XElement song = null;
            XElement sample = null;

            song = GetSong(artists, artistName, songAlbum, songTitle);

            if (song != null) {
                sample = CreateSampleNode(songSample, songSourceName, String.Empty, songSampleTime);
                song.Add(sample);
            } else {
                artist = GetArtist(artists, artistName);

                if (artist != null) {
                    release = GetRelease(artist, songAlbum);

                    if (release == null) {
                        release = CreateReleaseNode(songAlbum);
                        artist.Add(release);
                    }

                    song = CreateSongNode(songTitle);
                    sample = CreateSampleNode(songSample, songSourceName, String.Empty, songSampleTime);

                    song.Add(sample);
                    release.Add(song);
                } else {
                    artist = CreateArtistNode(artistName);
                    release = CreateReleaseNode(songAlbum);
                    song = CreateSongNode(songTitle);
                    sample = CreateSampleNode(songSample, songSourceName, String.Empty, songSampleTime);

                    song.Add(sample);
                    release.Add(song);
                    artist.Add(release);
                    artists.Add(artist);
                }
            }

            artists.Save("Samples.xml");
        }

        private void btnConvertSamples_Click(object sender, EventArgs e) {
            convertPagesWorker.RunWorkerAsync(txtConversionFolder.Text);
        }

        private void convertPagesWorker_DoWork(object sender, DoWorkEventArgs e) {
            string filePath = e.Argument.ToString().Substring(0, e.Argument.ToString().LastIndexOf(@"\"));

            XElement xmlDocument = XElement.Load(e.Argument.ToString());

            var links = from link in xmlDocument.Descendants("link")
                        select link;

            foreach (var link in links) {
                StreamReader fileReader = new StreamReader(e.Argument.ToString().Substring(0, e.Argument.ToString().LastIndexOf(@"\")) + @"\" + link.Attribute("href").Value.Substring(0, link.Attribute("href").Value.LastIndexOf('.')) + ".txt");
                string fileContents = fileReader.ReadToEnd();
                fileReader.Close();
                fileReader.Dispose();

                ProcessScrapedPages(filePath + @"\" + link.Attribute("href").Value.Substring(0, link.Attribute("href").Value.LastIndexOf('.')) + ".txt", fileContents);
            }
        }

        private void ProcessScrapedPages(string filePath, string fileContents) {
            //XElement xmlDocument = XElement.Load(filePath);

            Agility.HtmlDocument document = new HtmlAgilityPack.HtmlDocument();
            document.LoadHtml(fileContents);

            //Agility.HtmlNode artistNode = document.DocumentNode.SelectSingleNode("//div[@id='menu_artists']");
            //Agility.HtmlNodeCollection artists = artistNode.SelectNodes("//ul/li");

            Regex linkRegex = new Regex(@"\<li\>\<a href\=\x22(.*?)\x22\>(.*?)\<\/a\>\<\/li\>");

            Agility.HtmlNode artistNode = document.DocumentNode.SelectSingleNode("//div[@id='core']");
            Agility.HtmlNode artistNameNode = artistNode.SelectSingleNode("//h1");
            Agility.HtmlNodeCollection songs = artistNode.SelectNodes("//div[contains(@class,'sub')]");

            foreach (Agility.HtmlNode song in songs) {
                Agility.HtmlDocument songDocument = new HtmlAgilityPack.HtmlDocument();
                songDocument.LoadHtml(song.OuterHtml);

                Agility.HtmlNode songTitle = songDocument.DocumentNode.SelectSingleNode("//h2");
                Agility.HtmlNode songAlbum = songDocument.DocumentNode.SelectSingleNode("//span[@class='album']");

                Agility.HtmlNodeCollection sampleCollection = songDocument.DocumentNode.SelectNodes("//ul[@class='sample_source']");

                foreach (Agility.HtmlNode sampleNode in sampleCollection) {
                    Agility.HtmlDocument sampleNodeDocument = new HtmlAgilityPack.HtmlDocument();
                    sampleNodeDocument.LoadHtml(sampleNode.OuterHtml);

                    Agility.HtmlNode songSourceName = sampleNodeDocument.DocumentNode.SelectSingleNode("//ul/li/span/a[@class='source_name']");
                    Agility.HtmlNode songSampleTime = sampleNodeDocument.DocumentNode.SelectSingleNode("//ul/li/span[@class='sample_time']");
                    Agility.HtmlNode songSample = sampleNodeDocument.DocumentNode.SelectSingleNode("//ul/li/span[@class='sample']");
                    SaveSampleInformation(artistNameNode.InnerText, songTitle.InnerText, songAlbum.InnerText, songSampleTime.InnerText, songSample.InnerText, songSourceName.InnerText);
                }
            }

            string myString = string.Empty;
            //foreach (Agility.HtmlNode node in artists) {
            //    foreach (Match match in linkRegex.Matches(node.OuterHtml)) {
            //        string linkHref = match.Groups[1].Value;
            //        string linkText = match.Groups[2].Value;

            //        XElement linkElement = new XElement("link");
            //        XAttribute linkAttribute = new XAttribute("href", linkHref);
            //        XAttribute textAttribute = new XAttribute("text", linkText);
            //        linkElement.Add(linkAttribute);
            //        linkElement.Add(textAttribute);
            //        xmlDocument.Add(linkElement);
            //    }
            //}

            //xmlDocument.Save(filePath);
        }
    }
}
